﻿using System;
using System.Collections.Generic;
using SCG.General;
using SCG.SolarVengeanceEngine.Orders;

namespace SCG.SolarVengeanceEngine
{
    //An object that can exist on the SV Game map
    public class SVMapObject : SVObject
    {
        //constructor
        public SVMapObject(SVGame game)
            : base(game)
        {
            //add to internal list of map objects, and assigns ID
            if (Game != null)
            {
                game.RegisterMapObject(this);

                //initialize visibility flags, derived classes may assign first so check before setting to false
                foreach (Player p in Game.Players)
                {
                    if (!_visible.ContainsKey(p))
                        _visible[p] = false;
                    if (!_visTemp.ContainsKey(p))
                        _visTemp[p] = false;
                    if (!_discovered.ContainsKey(p))
                        _discovered[p] = false;
                }
            }
        }

        //Remove a map object, this causes it to be removed from the logical map too
        public virtual void Remove()
        {
            Game.DeRegisterMapObject(this);
            if (Game.InBounds(X, Y))
                Game.SetObjectAt(X, Y, null);

            //Clear orders of ships that can see this object and are targeting it
            foreach(StarShip ship in Game.StarShips)
                if (ship.Order != null && VisibleTo[ship.Owner])
                    if (ship.Order is MapObjectOrder)
                    {
                        MapObjectOrder moo = ship.Order as MapObjectOrder;
                        if (moo.MapObject == this)
                            ship.Order = null;
                    }
        }

        //The object's location on the map
        public int X
        {
            get
            {
                return _x;
            }
            protected internal set
            {
                _x = value;
            }
        }
        public int Y
        {
            get
            {
                return _y;
            }
            protected internal set
            {
                _y = value;
            }
        }

        //Access location as a Point
        public SCGPoint Location
        {
            get
            {
                return new SCGPoint(X, Y);
            }
        }

        //friendly name
        public virtual string DisplayName
        {
            get
            {
                return GetType().Name;
            }
        }

        //can you move into it (for patrol order purposes?
        public virtual bool CanPatrolInto
        {
            get
            {
                return false;
            }
        }

        //This is the public interface for an object's location to be set
        public virtual void SetLocation(int x, int y)
        {
            Game.SetObjectAt(_x, _y, null);
            _x = x;
            _y = y;
            Game.SetObjectAt(_x, _y, this);
        }
        public void SetLocation(double x, double y)
        {
            SetLocation((int)x, (int)y);
        }

        //Each map object maintains a unique ID for lookup purposes
        public int ID
        {
            get
            {
                return _ID;
            }
            internal set
            {
                _ID = value;
            }
        }

        //Is the map object currently visible to a player?
        public Dictionary<Player, bool> VisibleTo
        {
            get
            {
                return _visible;
            }
        }
        public bool VisibleToCurrentPlayer
        {
            get
            {
                if (Game.CurrentPlayer == null)
                    return true;
                else
                    return VisibleTo[Game.CurrentPlayer];
            }
        }

        //Can it pass visibility to allies?     
        public virtual bool PassesVisibilityToAllies
        {
            get
            {
                return true;
            }
        }

        //Was the map object discovered by a player?
        public Dictionary<Player, bool> DiscoveredBy
        {
            get
            {
                return _discovered;
            }
        }
        public virtual bool DiscoveredByCurrentPlayer
        {
            get
            {
                return Game.CurrentPlayer == null || DiscoveredBy[Game.CurrentPlayer];
            }
        }

        //Adjacency and distance calculation methods
        public bool IsAdjacent(SVMapObject svm)
        {
            return Math.Abs(X - svm.X) <= 1 && Math.Abs(Y - svm.Y) <= 1;
        }
        public bool IsAdjacent(int x, int y)
        {
            return Math.Abs(X - x) <= 1 && Math.Abs(Y - y) <= 1;
        }
        public bool IsAdjacent(SCGPoint pt)
        {
            return IsAdjacent(pt.X, pt.Y);
        }

        //Get the distance between this object and another one
        public int DistanceQuick(int x, int y)
        {
            return Math.Abs(X - x) + Math.Abs(Y - y);
        }
        public int DistanceQuick(SVMapObject svm)
        {
            return Math.Abs(X - svm.X) + Math.Abs(Y - svm.Y);
        }
        public double Distance(int x, int y)
        {
            double diffX = (x - X);
            double diffY = (y - Y);
            if (diffX == 0 && diffY == 0)
                return 0;
            return Math.Sqrt(diffX * diffX + diffY * diffY);
        }
        public double Distance(SVMapObject svm)
        {
            return Distance(svm.X, svm.Y);
        }

        //persistence
        public virtual string Persist()
        {
            SVTokenizer tok = new SVTokenizer(Game);         
            tok.Tokenize(X, Y, ID);
            foreach (Player p in Game.Players)
                tok.AppendToken(DiscoveredBy[p]);
            return tok.Result;
        }
        public virtual SVTokenizer Parse(string s)
        {
            SVTokenizer tok = new SVTokenizer(Game, s);
            int x = tok.TokenInt;
            int y = tok.TokenInt;
            ID = tok.TokenInt;            
            SetLocation(x, y);
            foreach (Player p in Game.Players)
                DiscoveredBy[p] = tok.TokenBoolean;
            return tok;
        }

        //private members        
        private int _x = -1;
        private int _y = -1;
        private int _ID;
        private Dictionary<Player, bool> _visible = new Dictionary<Player, bool>();
        private Dictionary<Player, bool> _visTemp = new Dictionary<Player, bool>();
        private Dictionary<Player, bool> _discovered = new Dictionary<Player, bool>();

        //access temporary visibility settings
        internal Dictionary<Player, bool> VisTemp
        {
            get
            {
                return _visTemp;
            }
        }
    }
}
